/**
 * Fzxa JavaScript Library v1.0
 *
 * @author fzxa http://www.fzxa.com or http://fzxa.googlecode.com
 * @copyright 2012 Fzxa Foundation and other contributors
 * Released under the MIT license
 * $Id: fzxa.js 37 2012-12-18 10:33:05Z thriftwang@gmail.com $
 */
void (function( window, document, undefined ) {

	if (!(typeof fzxa != 'undefined' && fzxa)) {

		var fzxa = FZXA = F = window.fzxa = [];

		(function(){
			//用于判定页面是否加载完毕
			fzxa.isReady = false;
			/**
			 * 推荐使ready方法，不使用window.load方法
			 * @namespace fzxa.ready
			 * @param {function}
			 */
			fzxa.ready = function (fn) {
				if (fzxa.isReady) {
					fn();
				} else {
					fzxa.push(fn);
				}
			}
			//执行所有在window.onload之前放入的函数
			fzxa.fireReady = function () {
				if (!fzxa.isReady) {
					if (!document.body) {
						return setTimeout(fzxa.fireReady, 16);
					}
					fzxa.isReady = 1;
					if (fzxa.length) {
						for (var i = 0, fn; fn = fzxa[i]; i++)
							fn();
					}
				}
			}
			
			//开始初始化fzxaReady函数，判定页面的加载情况
			if (document.readyState === "complete") {
				fzxa.fireReady();
			} else if ( - [1, ]) {
				document.addEventListener("DOMContentLoaded", function () {
					document.removeEventListener("DOMContentLoaded", arguments.callee, false);
					fzxa.fireReady();
				}, false);
			} else {
				//当页面包含图片时，onreadystatechange事件会触发在window.onload之后，
				//换言之，它只能正确地执行于页面不包含二进制资源或非常少或者被缓存时
				document.attachEvent("onreadystatechange", function () {
					if (document.readyState == "complete") {
						document.detachEvent("onreadystatechange", arguments.callee);
						fzxa.fireReady();
					}
				});
				(function () {
					if (fzxa.isReady) {
						return;
					}
					//doScroll存在于所有标签而不管其是否支持滚动条
					//这里如果用document.documentElement.doScroll()，我们需要判定其是否位于顶层document
					var node = new Image
					try {
						node.doScroll();
						node = null //防止IE内存泄漏
					} catch (e) {
						//javascrpt最短时钟间隔为16ms，这里取其倍数
						//http://blog.csdn.net/aimingoo/archive/2006/12/21/1451556.aspx
						setTimeout(arguments.callee, 64);
						return;
					}
					fzxa.fireReady();
				})();
			}
		})();
		

		/**
		 * 异步加载脚本
		 * @method loadJs
		 * @static
		 * @param { String } url Javascript文件路径
		 * @param { Function } callback (Optional) Javascript加载后的回调函数
		 * @param { Option } options (Optional) 配置选项，例如charset
		 */
		fzxa.loadJs = function(url, callback, options) {
			options = options || {};
			var head = document.getElementsByTagName('head')[0] || document.documentElement,
				script = document.createElement('script'),
				done = false;
			script.src = url;
			if (options.charset) {
				script.charset = options.charset;
			}
			script.onerror = script.onload = script.onreadystatechange = function() {
				if (!done && (!this.readyState || this.readyState == "loaded" || this.readyState == "complete")) {
					done = true;
					if (callback) {
						callback();
					}
					script.onerror = script.onload = script.onreadystatechange = null;
					head.removeChild(script);
				}
			};
			head.insertBefore(script, head.firstChild);
		}

		/**
		 * 加载css样式表
		 * @method loadCss
		 * @static
		 * @param { String } url Css文件路径
		 */
		fzxa.loadCss = function(url) {
			var head = document.getElementsByTagName('head')[0] || document.documentElement,
			css = document.createElement('link');
			css.rel = 'stylesheet';
			css.type = 'text/css';
			css.href = url;
			head.insertBefore(css, head.firstChild);
		}

		/**
		 * 对XMLHttpRequest请求的封装
		 * @namespace ajax.ajax
		 */
		fzxa.ajax = fzxa.ajax || {};

		fzxa.ajax.request = function (url, opt_options) {
			var options     = opt_options || {},
				data        = options.data || "",
				async       = !(options.async === false),
				username    = options.username || "",
				password    = options.password || "",
				method      = (options.method || "GET").toUpperCase(),
				headers     = options.headers || {},
				// 基本的逻辑来自lili同学提供的patch
				timeout     = options.timeout || 0,
				eventHandlers = {},
				tick, key, xhr;
			/**
			 * readyState发生变更时调用
			 * 
			 * @ignore
			 */
			function stateChangeHandler() {
				if (xhr.readyState == 4) {
					try {
						var stat = xhr.status;
					} catch (ex) {
						// 在请求时，如果网络中断，Firefox会无法取得status
						fire('failure');
						return;
					}
					
					fire(stat);
					
					// http://www.never-online.net/blog/article.asp?id=261
					// case 12002: // Server timeout      
					// case 12029: // dropped connections
					// case 12030: // dropped connections
					// case 12031: // dropped connections
					// case 12152: // closed by server
					// case 13030: // status and statusText are unavailable
					
					// IE error sometimes returns 1223 when it 
					// should be 204, so treat it as success
					if ((stat >= 200 && stat < 300)
						|| stat == 304
						|| stat == 1223) {
						fire('success');
					} else {
						fire('failure');
					}
					
					/*
					 * NOTE: Testing discovered that for some bizarre reason, on Mozilla, the
					 * JavaScript <code>XmlHttpRequest.onreadystatechange</code> handler
					 * function maybe still be called after it is deleted. The theory is that the
					 * callback is cached somewhere. Setting it to null or an empty function does
					 * seem to work properly, though.
					 * 
					 * On IE, there are two problems: Setting onreadystatechange to null (as
					 * opposed to an empty function) sometimes throws an exception. With
					 * particular (rare) versions of jscript.dll, setting onreadystatechange from
					 * within onreadystatechange causes a crash. Setting it from within a timeout
					 * fixes this bug (see issue 1610).
					 * 
					 * End result: *always* set onreadystatechange to an empty function (never to
					 * null). Never set onreadystatechange from within onreadystatechange (always
					 * in a setTimeout()).
					 */
					window.setTimeout(
						function() {
							// 避免内存泄露.
							// 由new Function改成不含此作用域链的 fzxa.fn.blank 函数,
							// 以避免作用域链带来的隐性循环引用导致的IE下内存泄露. By rocy 2011-01-05 .
							xhr.onreadystatechange = function(){};
							if (async) {
								xhr = null;
							}
						}, 0);
				}
			}
			
			/**
			 * 获取XMLHttpRequest对象
			 * 
			 * @ignore
			 * @return {XMLHttpRequest} XMLHttpRequest对象
			 */
			function getXHR() {
				if (window.ActiveXObject) {
					try {
						return new ActiveXObject("Msxml2.XMLHTTP");
					} catch (e) {
						try {
							return new ActiveXObject("Microsoft.XMLHTTP");
						} catch (e) {}
					}
				}
				if (window.XMLHttpRequest) {
					return new XMLHttpRequest();
				}
			}
			
			/**
			 * 触发事件
			 * 
			 * @ignore
			 * @param {String} type 事件类型
			 */
			function fire(type) {
				type = 'on' + type;
				var handler = eventHandlers[type],
					globelHandler = fzxa.ajax[type];
				
				// 不对事件类型进行验证
				if (handler) {
					if (tick) {
					  clearTimeout(tick);
					}
					if (type != 'onsuccess') {
						handler(xhr);
					} else {
						//处理获取xhr.responseText导致出错的情况,比如请求图片地址.
						try {
							xhr.responseText;
						} catch(error) {
							return handler(xhr);
						}
						handler(xhr, xhr.responseText);
					}
				} else if (globelHandler) {
					//onsuccess不支持全局事件
					if (type == 'onsuccess') {
						return;
					}
					globelHandler(xhr);
				}
			}
			

			for (key in options) {
				// 将options参数中的事件参数复制到eventHandlers对象中
				// 这里复制所有options的成员，eventHandlers有冗余
				// 但是不会产生任何影响，并且代码紧凑
				eventHandlers[key] = options[key];
			}

			headers['X-Requested-With'] = 'XMLHttpRequest';
			
			
			try {
				xhr = getXHR();

				if (method == 'GET') {
					if (data) {
						url += (url.indexOf('?') >= 0 ? '&' : '?') + data;
						data = null;
					}
					if(options['noCache']){
						url += (url.indexOf('?') >= 0 ? '&' : '?') + 'b' + (+ new Date) + '=1';
					}
				}
				
				if (username) {
					xhr.open(method, url, async, username, password);
				} else {
					xhr.open(method, url, async);
				}
				
				if (async) {
					xhr.onreadystatechange = stateChangeHandler;
				}
				
				// 在open之后再进行http请求头设定
				// FIXME 是否需要添加; charset=UTF-8呢
				if (method == 'POST') {
					xhr.setRequestHeader("Content-Type",
						(headers['Content-Type'] || "application/x-www-form-urlencoded"));
				}
				
				for (key in headers) {
					if (headers.hasOwnProperty(key)) {
						xhr.setRequestHeader(key, headers[key]);
					}
				}

				fire('beforerequest');

				if (timeout) {
					tick = setTimeout(function(){
						xhr.onreadystatechange = function(){};
						xhr.abort();
						fire("timeout");
					}, timeout);
				}
				xhr.send(data);
				
				if (!async) {
					stateChangeHandler();
				}
			} catch (ex) {
				fire('failure');
			}
			
			return xhr;
		};

		/**
		 * 操作cookie的方法
		 * @namespace fzxa.cookie
		 */
		fzxa.cookie = fzxa.cookie || {};
		/**
		 * 验证字符串是否合法的cookie键名
		 * 
		 * @param {string} source 需要遍历的数组
		 * @meta standard
		 * @return {boolean} 是否合法的cookie键名
		 */
		fzxa.cookie._isValidKey = function (key) {
			// http://www.w3.org/Protocols/rfc2109/rfc2109
			// Syntax:  General
			// The two state management headers, Set-Cookie and Cookie, have common
			// syntactic properties involving attribute-value pairs.  The following
			// grammar uses the notation, and tokens DIGIT (decimal digits) and
			// token (informally, a sequence of non-special, non-white space
			// characters) from the HTTP/1.1 specification [RFC 2068] to describe
			// their syntax.
			// av-pairs   = av-pair *(";" av-pair)
			// av-pair    = attr ["=" value] ; optional value
			// attr       = token
			// value      = word
			// word       = token | quoted-string
			
			// http://www.ietf.org/rfc/rfc2068.txt
			// token      = 1*<any CHAR except CTLs or tspecials>
			// CHAR       = <any US-ASCII character (octets 0 - 127)>
			// CTL        = <any US-ASCII control character
			//              (octets 0 - 31) and DEL (127)>
			// tspecials  = "(" | ")" | "<" | ">" | "@"
			//              | "," | ";" | ":" | "\" | <">
			//              | "/" | "[" | "]" | "?" | "="
			//              | "{" | "}" | SP | HT
			// SP         = <US-ASCII SP, space (32)>
			// HT         = <US-ASCII HT, -thorizontalab (9)>
			return (new RegExp("^[^\\x00-\\x20\\x7f\\(\\)<>@,;:\\\\\\\"\\[\\]\\?=\\{\\}\\/\\u0080-\\uffff]+\x24")).test(key);
		};

		/**
		 * 获取cookie的值，不对值进行解码
		 * @name fzxa.cookie.getRaw
		 * @function
		 * @grammar fzxa.cookie.getRaw(key)
		 * @param {string} key 需要获取Cookie的键名
		 * @meta standard
		 * @see fzxa.cookie.get,fzxa.cookie.setRaw
		 *             
		 * @returns {string|null} 获取的Cookie值，获取不到时返回null
		 */
		fzxa.cookie.getRaw = function (key) {
			if (fzxa.cookie._isValidKey(key)) {
				var reg = new RegExp("(^| )" + key + "=([^;]*)(;|\x24)"),
					result = reg.exec(document.cookie);
					
				if (result) {
					return result[2] || null;
				}
			}

			return null;
		};

		/**
		 * 获取cookie的值，用decodeURIComponent进行解码
		 * @name fzxa.cookie.get
		 * @function
		 * @grammar fzxa.cookie.get(key)
		 * @param {string} key 需要获取Cookie的键名
		 * @remark
		 * <b>注意：</b>该方法会对cookie值进行decodeURIComponent解码。如果想获得cookie源字符串，请使用getRaw方法。
		 * @meta standard
		 * @see fzxa.cookie.getRaw,fzxa.cookie.set
		 *             
		 * @returns {string|null} cookie的值，获取不到时返回null
		 */
		fzxa.cookie.get = function (key) {
			var value = fzxa.cookie.getRaw(key);
			if ('string' == typeof value) {
				value = decodeURIComponent(value);
				return value;
			}
			return null;
		};

		/**
		 * 设置cookie的值，不对值进行编码
		 * @name fzxa.cookie.setRaw
		 * @function
		 * @grammar fzxa.cookie.setRaw(key, value[, options])
		 * @param {string} key 需要设置Cookie的键名
		 * @param {string} value 需要设置Cookie的值
		 * @param {Object} [options] 设置Cookie的其他可选参数
		 * @config {string} [path] cookie路径
		 * @config {Date|number} [expires] cookie过期时间,如果类型是数字的话, 单位是毫秒
		 * @config {string} [domain] cookie域名
		 * @config {string} [secure] cookie是否安全传输
		 * @remark
		 * 
		<b>options参数包括：</b><br>
		path:cookie路径<br>
		expires:cookie过期时间，Number型，单位为毫秒。<br>
		domain:cookie域名<br>
		secure:cookie是否安全传输
				
		 * @meta standard
		 * @see fzxa.cookie.set,fzxa.cookie.getRaw
		 */
		fzxa.cookie.setRaw = function (key, value, options) {
			if (!fzxa.cookie._isValidKey(key)) {
				return;
			}
			
			options = options || {};
			//options.path = options.path || "/"; // meizz 20100402 设定一个初始值，方便后续的操作
			//berg 20100409 去掉，因为用户希望默认的path是当前路径，这样和浏览器对cookie的定义也是一致的
			
			// 计算cookie过期时间
			var expires = options.expires;
			if ('number' == typeof options.expires) {
				expires = new Date();
				expires.setTime(expires.getTime() + options.expires);
			}
			
			document.cookie =
				key + "=" + value
				+ (options.path ? "; path=" + options.path : "")
				+ (expires ? "; expires=" + expires.toGMTString() : "")
				+ (options.domain ? "; domain=" + options.domain : "")
				+ (options.secure ? "; secure" : ''); 
		};

		/**
		 * 删除cookie的值
		 * @name fzxa.cookie.remove
		 * @function
		 * @grammar fzxa.cookie.remove(key, options)
		 * @param {string} key 需要删除Cookie的键名
		 * @param {Object} options 需要删除的cookie对应的 path domain 等值
		 * @meta standard
		 */
		fzxa.cookie.remove = function (key, options) {
			options = options || {};
			options.expires = new Date(0);
			fzxa.cookie.setRaw(key, '', options);
		};
		/**
		 * 设置cookie的值，用encodeURIComponent进行编码
		 * @name fzxa.cookie.set
		 * @function
		 * @grammar fzxa.cookie.set(key, value[, options])
		 * @param {string} key 需要设置Cookie的键名
		 * @param {string} value 需要设置Cookie的值
		 * @param {Object} [options] 设置Cookie的其他可选参数
		 * @config {string} [path] cookie路径
		 * @config {Date|number} [expires] cookie过期时间,如果类型是数字的话, 单位是毫秒
		 * @config {string} [domain] cookie域名
		 * @config {string} [secure] cookie是否安全传输
		 * @remark
		 * 
		1. <b>注意：</b>该方法会对cookie值进行encodeURIComponent编码。如果想设置cookie源字符串，请使用setRaw方法。<br><br>
		2. <b>options参数包括：</b><br>
		path:cookie路径<br>
		expires:cookie过期时间，Number型，单位为毫秒。<br>
		domain:cookie域名<br>
		secure:cookie是否安全传输
				
		 * @meta standard
		 * @see fzxa.cookie.setRaw,fzxa.cookie.get
		 */
		fzxa.cookie.set = function (key, value, options) {
			fzxa.cookie.setRaw(key, encodeURIComponent(value), options);
		};

		/**
		 * 操作数组的方法
		 * @namespace fzxa.array
		 */

		fzxa.array = fzxa.array = {};

		/**
		 * 遍历数组中所有元素
		 * @name fzxa.array.each
		 * @function
		 * @grammar fzxa.array.each(source, iterator[, thisObject])
		 * @param {Array} source 需要遍历的数组
		 * @param {Function} iterator 对每个数组元素进行调用的函数，该函数有两个参数，第一个为数组元素，第二个为数组索引值，function (item, index)。
		 * @param {Object} [thisObject] 函数调用时的this指针，如果没有此参数，默认是当前遍历的数组
		 * @remark
		 * each方法不支持对Object的遍历,对Object的遍历使用fzxa.object.each 。
		 * @shortcut each
		 * @meta standard
		 * 
		 * @returns {Array} 遍历的数组
		 */
		fzxa.each = fzxa.array.forEach = fzxa.array.each = function (source, iterator, thisObject) {
			var returnValue, item, i, len = source.length;
			
			if (typeof iterator == 'function') {
				for (i = 0; i < len; i++) {
					item = source[i];
					//TODO
					//此处实现和标准不符合，标准中是这样说的：
					//If a thisObject parameter is provided to forEach, it will be used as the this for each invocation of the callback. If it is not provided, or is null, the global object associated with callback is used instead.
					returnValue = iterator.call(thisObject || source, item, i);

					if (returnValue === false) {
						break;
					}
				}
			}
			return source;
		};

		/**
		 * 过滤数组中的相同项。如果两个元素相同，会删除后一个元素。
		 * @name fzxa.array.unique
		 * @function
		 * @grammar fzxa.array.unique(source[, compareFn])
		 * @param {Array} source 需要过滤相同项的数组
		 * @param {Function} [compareFn] 比较两个数组项是否相同的函数,两个数组项作为函数的参数。
		 *             
		 * @returns {Array} 过滤后的新数组
		 */
		fzxa.array.unique = function (source, compareFn) {
			var len = source.length,
				result = source.slice(0),
				i, datum;
				
			if ('function' != typeof compareFn) {
				compareFn = function (item1, item2) {
					return item1 === item2;
				};
			}
			
			// 从后往前双重循环比较
			// 如果两个元素相同，删除后一个
			while (--len > 0) {
				datum = result[len];
				i = len;
				while (i--) {
					if (compareFn(datum, result[i])) {
						result.splice(len, 1);
						break;
					}
				}
			}
			return result;
		};

		/**
		 * 操作cookie的方法
		 * @namespace fzxa.cookie
		 */
		fzxa.Browser = fzxa.Browser = {};

		fzxa.Browser = function() {
			var na = window.navigator,
				ua = na.userAgent.toLowerCase(),
				browserTester = /(msie|webkit|gecko|presto|opera|safari|firefox|chrome|maxthon|android|ipad|iphone|webos|hpwos)[ \/os]*([\d_.]+)/ig,
				Browser = {
					platform: na.platform
				};
			ua.replace(browserTester, function(a, b, c) {
				var bLower = b.toLowerCase();
				if (!Browser[bLower]) {
					Browser[bLower] = c; 
				}
			});
			if (Browser.opera) { //Opera9.8后版本号位置变化
				ua.replace(/opera.*version\/([\d.]+)/, function(a, b) {
					Browser.opera = b;
				});
			}
			if (Browser.msie) {
				Browser.ie = Browser.msie;
				var v = parseInt(Browser.msie, 10);
				Browser['ie' + v] = true;
			}
			return Browser;
		}

		/**
		 * 操作dom的方法
		 * @namespace fzxa.dom 
		 */
		fzxa.dom = fzxa.dom || {};

		/**
		 * 提供css选择器功能   选择器支持所有的<a href="http://www.w3.org/TR/css3-selectors/">css3选择器</a> ，核心实现采用sizzle。fzxa.dom.query.matches 请参考<a href="http://wiki.github.com/jeresig/sizzle/" target="_blank">sizzle 文档</a> 
		 * @name fzxa.dom.query
		 * @function
		 * @grammar fzxa.dom.query(selector[, context, results])
		 * @param {String} selector 选择器定义
		 * @param {HTMLElement | DOMDocument} [context] 查找的上下文
		 * @param {Array} [results] 查找的结果会追加到这个数组中
		 * @version 1.5
		 * @remark
		 * 
					选择器支持所有的<a href="http://www.w3.org/TR/css3-selectors/">css3选择器</a> ，核心实现采用sizzle。可参考<a href="https://github.com/jquery/sizzle/wiki/Sizzle-Home" target="_blank">sizzle 文档</a>
				
		 * @see fzxa.dom.g, fzxa.dom.q,
		 * @returns {Array}        包含所有筛选出的DOM元素的数组
		 */

		(function(){

		var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
			expando = "sizcache" + (Math.random() + '').replace('.', ''),
			done = 0,
			toString = Object.prototype.toString,
			hasDuplicate = false,
			baseHasDuplicate = true,
			rBackslash = /\\/g,
			rReturn = /\r\n/g,
			rNonWord = /\W/;

		// Here we check if the JavaScript engine is using some sort of
		// optimization where it does not always call our comparision
		// function. If that is the case, discard the hasDuplicate value.
		//   Thus far that includes Google Chrome.
		[0, 0].sort(function() {
			baseHasDuplicate = false;
			return 0;
		});

		var Sizzle = function( selector, context, results, seed ) {
			results = results || [];
			context = context || document;

			var origContext = context;

			if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
				return [];
			}
			
			if ( !selector || typeof selector !== "string" ) {
				return results;
			}

			var m, set, checkSet, extra, ret, cur, pop, i,
				prune = true,
				contextXML = Sizzle.isXML( context ),
				parts = [],
				soFar = selector;
			
			// Reset the position of the chunker regexp (start from head)
			do {
				chunker.exec( "" );
				m = chunker.exec( soFar );

				if ( m ) {
					soFar = m[3];
				
					parts.push( m[1] );
				
					if ( m[2] ) {
						extra = m[3];
						break;
					}
				}
			} while ( m );

			if ( parts.length > 1 && origPOS.exec( selector ) ) {

				if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
					set = posProcess( parts[0] + parts[1], context, seed );

				} else {
					set = Expr.relative[ parts[0] ] ?
						[ context ] :
						Sizzle( parts.shift(), context );

					while ( parts.length ) {
						selector = parts.shift();

						if ( Expr.relative[ selector ] ) {
							selector += parts.shift();
						}
						
						set = posProcess( selector, set, seed );
					}
				}

			} else {
				// Take a shortcut and set the context if the root selector is an ID
				// (but not if it'll be faster if the inner selector is an ID)
				if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
						Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {

					ret = Sizzle.find( parts.shift(), context, contextXML );
					context = ret.expr ?
						Sizzle.filter( ret.expr, ret.set )[0] :
						ret.set[0];
				}

				if ( context ) {
					ret = seed ?
						{ expr: parts.pop(), set: makeArray(seed) } :
						Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );

					set = ret.expr ?
						Sizzle.filter( ret.expr, ret.set ) :
						ret.set;

					if ( parts.length > 0 ) {
						checkSet = makeArray( set );

					} else {
						prune = false;
					}

					while ( parts.length ) {
						cur = parts.pop();
						pop = cur;

						if ( !Expr.relative[ cur ] ) {
							cur = "";
						} else {
							pop = parts.pop();
						}

						if ( pop == null ) {
							pop = context;
						}

						Expr.relative[ cur ]( checkSet, pop, contextXML );
					}

				} else {
					checkSet = parts = [];
				}
			}

			if ( !checkSet ) {
				checkSet = set;
			}

			if ( !checkSet ) {
				Sizzle.error( cur || selector );
			}

			if ( toString.call(checkSet) === "[object Array]" ) {
				if ( !prune ) {
					results.push.apply( results, checkSet );

				} else if ( context && context.nodeType === 1 ) {
					for ( i = 0; checkSet[i] != null; i++ ) {
						if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {
							results.push( set[i] );
						}
					}

				} else {
					for ( i = 0; checkSet[i] != null; i++ ) {
						if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
							results.push( set[i] );
						}
					}
				}

			} else {
				makeArray( checkSet, results );
			}

			if ( extra ) {
				Sizzle( extra, origContext, results, seed );
				Sizzle.uniqueSort( results );
			}

			return results;
		};

		Sizzle.uniqueSort = function( results ) {
			if ( sortOrder ) {
				hasDuplicate = baseHasDuplicate;
				results.sort( sortOrder );

				if ( hasDuplicate ) {
					for ( var i = 1; i < results.length; i++ ) {
						if ( results[i] === results[ i - 1 ] ) {
							results.splice( i--, 1 );
						}
					}
				}
			}

			return results;
		};

		Sizzle.matches = function( expr, set ) {
			return Sizzle( expr, null, null, set );
		};

		Sizzle.matchesSelector = function( node, expr ) {
			return Sizzle( expr, null, null, [node] ).length > 0;
		};

		Sizzle.find = function( expr, context, isXML ) {
			var set, i, len, match, type, left;

			if ( !expr ) {
				return [];
			}

			for ( i = 0, len = Expr.order.length; i < len; i++ ) {
				type = Expr.order[i];
				
				if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
					left = match[1];
					match.splice( 1, 1 );

					if ( left.substr( left.length - 1 ) !== "\\" ) {
						match[1] = (match[1] || "").replace( rBackslash, "" );
						set = Expr.find[ type ]( match, context, isXML );

						if ( set != null ) {
							expr = expr.replace( Expr.match[ type ], "" );
							break;
						}
					}
				}
			}

			if ( !set ) {
				set = typeof context.getElementsByTagName !== "undefined" ?
					context.getElementsByTagName( "*" ) :
					[];
			}

			return { set: set, expr: expr };
		};

		Sizzle.filter = function( expr, set, inplace, not ) {
			var match, anyFound,
				type, found, item, filter, left,
				i, pass,
				old = expr,
				result = [],
				curLoop = set,
				isXMLFilter = set && set[0] && Sizzle.isXML( set[0] );

			while ( expr && set.length ) {
				for ( type in Expr.filter ) {
					if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
						filter = Expr.filter[ type ];
						left = match[1];

						anyFound = false;

						match.splice(1,1);

						if ( left.substr( left.length - 1 ) === "\\" ) {
							continue;
						}

						if ( curLoop === result ) {
							result = [];
						}

						if ( Expr.preFilter[ type ] ) {
							match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );

							if ( !match ) {
								anyFound = found = true;

							} else if ( match === true ) {
								continue;
							}
						}

						if ( match ) {
							for ( i = 0; (item = curLoop[i]) != null; i++ ) {
								if ( item ) {
									found = filter( item, match, i, curLoop );
									pass = not ^ found;

									if ( inplace && found != null ) {
										if ( pass ) {
											anyFound = true;

										} else {
											curLoop[i] = false;
										}

									} else if ( pass ) {
										result.push( item );
										anyFound = true;
									}
								}
							}
						}

						if ( found !== undefined ) {
							if ( !inplace ) {
								curLoop = result;
							}

							expr = expr.replace( Expr.match[ type ], "" );

							if ( !anyFound ) {
								return [];
							}

							break;
						}
					}
				}

				// Improper expression
				if ( expr === old ) {
					if ( anyFound == null ) {
						Sizzle.error( expr );

					} else {
						break;
					}
				}

				old = expr;
			}

			return curLoop;
		};

		Sizzle.error = function( msg ) {
			throw "Syntax error, unrecognized expression: " + msg;
		};

		/**
		 * Utility function for retreiving the text value of an array of DOM nodes
		 * @param {Array|Element} elem
		 */
		var getText = Sizzle.getText = function( elem ) {
			var i, node,
				nodeType = elem.nodeType,
				ret = "";

			if ( nodeType ) {
				if ( nodeType === 1 ) {
					// Use textContent || innerText for elements
					if ( typeof elem.textContent === 'string' ) {
						return elem.textContent;
					} else if ( typeof elem.innerText === 'string' ) {
						// Replace IE's carriage returns
						return elem.innerText.replace( rReturn, '' );
					} else {
						// Traverse it's children
						for ( elem = elem.firstChild; elem; elem = elem.nextSibling) {
							ret += getText( elem );
						}
					}
				} else if ( nodeType === 3 || nodeType === 4 ) {
					return elem.nodeValue;
				}
			} else {

				// If no nodeType, this is expected to be an array
				for ( i = 0; (node = elem[i]); i++ ) {
					// Do not traverse comment nodes
					if ( node.nodeType !== 8 ) {
						ret += getText( node );
					}
				}
			}
			return ret;
		};

		var Expr = Sizzle.selectors = {
			order: [ "ID", "NAME", "TAG" ],

			match: {
				ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
				CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
				NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,
				ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,
				TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,
				CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,
				POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,
				PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
			},

			leftMatch: {},

			attrMap: {
				"class": "className",
				"for": "htmlFor"
			},

			attrHandle: {
				href: function( elem ) {
					return elem.getAttribute( "href" );
				},
				type: function( elem ) {
					return elem.getAttribute( "type" );
				}
			},

			relative: {
				"+": function(checkSet, part){
					var isPartStr = typeof part === "string",
						isTag = isPartStr && !rNonWord.test( part ),
						isPartStrNotTag = isPartStr && !isTag;

					if ( isTag ) {
						part = part.toLowerCase();
					}

					for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
						if ( (elem = checkSet[i]) ) {
							while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}

							checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
								elem || false :
								elem === part;
						}
					}

					if ( isPartStrNotTag ) {
						Sizzle.filter( part, checkSet, true );
					}
				},

				">": function( checkSet, part ) {
					var elem,
						isPartStr = typeof part === "string",
						i = 0,
						l = checkSet.length;

					if ( isPartStr && !rNonWord.test( part ) ) {
						part = part.toLowerCase();

						for ( ; i < l; i++ ) {
							elem = checkSet[i];

							if ( elem ) {
								var parent = elem.parentNode;
								checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
							}
						}

					} else {
						for ( ; i < l; i++ ) {
							elem = checkSet[i];

							if ( elem ) {
								checkSet[i] = isPartStr ?
									elem.parentNode :
									elem.parentNode === part;
							}
						}

						if ( isPartStr ) {
							Sizzle.filter( part, checkSet, true );
						}
					}
				},

				"": function(checkSet, part, isXML){
					var nodeCheck,
						doneName = done++,
						checkFn = dirCheck;

					if ( typeof part === "string" && !rNonWord.test( part ) ) {
						part = part.toLowerCase();
						nodeCheck = part;
						checkFn = dirNodeCheck;
					}

					checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML );
				},

				"~": function( checkSet, part, isXML ) {
					var nodeCheck,
						doneName = done++,
						checkFn = dirCheck;

					if ( typeof part === "string" && !rNonWord.test( part ) ) {
						part = part.toLowerCase();
						nodeCheck = part;
						checkFn = dirNodeCheck;
					}

					checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML );
				}
			},

			find: {
				ID: function( match, context, isXML ) {
					if ( typeof context.getElementById !== "undefined" && !isXML ) {
						var m = context.getElementById(match[1]);
						// Check parentNode to catch when Blackberry 4.6 returns
						// nodes that are no longer in the document #6963
						return m && m.parentNode ? [m] : [];
					}
				},

				NAME: function( match, context ) {
					if ( typeof context.getElementsByName !== "undefined" ) {
						var ret = [],
							results = context.getElementsByName( match[1] );

						for ( var i = 0, l = results.length; i < l; i++ ) {
							if ( results[i].getAttribute("name") === match[1] ) {
								ret.push( results[i] );
							}
						}

						return ret.length === 0 ? null : ret;
					}
				},

				TAG: function( match, context ) {
					if ( typeof context.getElementsByTagName !== "undefined" ) {
						return context.getElementsByTagName( match[1] );
					}
				}
			},
			preFilter: {
				CLASS: function( match, curLoop, inplace, result, not, isXML ) {
					match = " " + match[1].replace( rBackslash, "" ) + " ";

					if ( isXML ) {
						return match;
					}

					for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
						if ( elem ) {
							if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) {
								if ( !inplace ) {
									result.push( elem );
								}

							} else if ( inplace ) {
								curLoop[i] = false;
							}
						}
					}

					return false;
				},

				ID: function( match ) {
					return match[1].replace( rBackslash, "" );
				},

				TAG: function( match, curLoop ) {
					return match[1].replace( rBackslash, "" ).toLowerCase();
				},

				CHILD: function( match ) {
					if ( match[1] === "nth" ) {
						if ( !match[2] ) {
							Sizzle.error( match[0] );
						}

						match[2] = match[2].replace(/^\+|\s*/g, '');

						// parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
						var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec(
							match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
							!/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);

						// calculate the numbers (first)n+(last) including if they are negative
						match[2] = (test[1] + (test[2] || 1)) - 0;
						match[3] = test[3] - 0;
					}
					else if ( match[2] ) {
						Sizzle.error( match[0] );
					}

					// TODO: Move to normal caching system
					match[0] = done++;

					return match;
				},

				ATTR: function( match, curLoop, inplace, result, not, isXML ) {
					var name = match[1] = match[1].replace( rBackslash, "" );
					
					if ( !isXML && Expr.attrMap[name] ) {
						match[1] = Expr.attrMap[name];
					}

					// Handle if an un-quoted value was used
					match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" );

					if ( match[2] === "~=" ) {
						match[4] = " " + match[4] + " ";
					}

					return match;
				},

				PSEUDO: function( match, curLoop, inplace, result, not ) {
					if ( match[1] === "not" ) {
						// If we're dealing with a complex expression, or a simple one
						if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
							match[3] = Sizzle(match[3], null, null, curLoop);

						} else {
							var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);

							if ( !inplace ) {
								result.push.apply( result, ret );
							}

							return false;
						}

					} else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
						return true;
					}
					
					return match;
				},

				POS: function( match ) {
					match.unshift( true );

					return match;
				}
			},
			
			filters: {
				enabled: function( elem ) {
					return elem.disabled === false && elem.type !== "hidden";
				},

				disabled: function( elem ) {
					return elem.disabled === true;
				},

				checked: function( elem ) {
					return elem.checked === true;
				},
				
				selected: function( elem ) {
					// Accessing this property makes selected-by-default
					// options in Safari work properly
					if ( elem.parentNode ) {
						elem.parentNode.selectedIndex;
					}
					
					return elem.selected === true;
				},

				parent: function( elem ) {
					return !!elem.firstChild;
				},

				empty: function( elem ) {
					return !elem.firstChild;
				},

				has: function( elem, i, match ) {
					return !!Sizzle( match[3], elem ).length;
				},

				header: function( elem ) {
					return (/h\d/i).test( elem.nodeName );
				},

				text: function( elem ) {
					var attr = elem.getAttribute( "type" ), type = elem.type;
					// IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) 
					// use getAttribute instead to test this case
					return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null );
				},

				radio: function( elem ) {
					return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type;
				},

				checkbox: function( elem ) {
					return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type;
				},

				file: function( elem ) {
					return elem.nodeName.toLowerCase() === "input" && "file" === elem.type;
				},

				password: function( elem ) {
					return elem.nodeName.toLowerCase() === "input" && "password" === elem.type;
				},

				submit: function( elem ) {
					var name = elem.nodeName.toLowerCase();
					return (name === "input" || name === "button") && "submit" === elem.type;
				},

				image: function( elem ) {
					return elem.nodeName.toLowerCase() === "input" && "image" === elem.type;
				},

				reset: function( elem ) {
					var name = elem.nodeName.toLowerCase();
					return (name === "input" || name === "button") && "reset" === elem.type;
				},

				button: function( elem ) {
					var name = elem.nodeName.toLowerCase();
					return name === "input" && "button" === elem.type || name === "button";
				},

				input: function( elem ) {
					return (/input|select|textarea|button/i).test( elem.nodeName );
				},

				focus: function( elem ) {
					return elem === elem.ownerDocument.activeElement;
				}
			},
			setFilters: {
				first: function( elem, i ) {
					return i === 0;
				},

				last: function( elem, i, match, array ) {
					return i === array.length - 1;
				},

				even: function( elem, i ) {
					return i % 2 === 0;
				},

				odd: function( elem, i ) {
					return i % 2 === 1;
				},

				lt: function( elem, i, match ) {
					return i < match[3] - 0;
				},

				gt: function( elem, i, match ) {
					return i > match[3] - 0;
				},

				nth: function( elem, i, match ) {
					return match[3] - 0 === i;
				},

				eq: function( elem, i, match ) {
					return match[3] - 0 === i;
				}
			},
			filter: {
				PSEUDO: function( elem, match, i, array ) {
					var name = match[1],
						filter = Expr.filters[ name ];

					if ( filter ) {
						return filter( elem, i, match, array );

					} else if ( name === "contains" ) {
						return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0;

					} else if ( name === "not" ) {
						var not = match[3];

						for ( var j = 0, l = not.length; j < l; j++ ) {
							if ( not[j] === elem ) {
								return false;
							}
						}

						return true;

					} else {
						Sizzle.error( name );
					}
				},

				CHILD: function( elem, match ) {
					var first, last,
						doneName, parent, cache,
						count, diff,
						type = match[1],
						node = elem;

					switch ( type ) {
						case "only":
						case "first":
							while ( (node = node.previousSibling) )	 {
								if ( node.nodeType === 1 ) { 
									return false; 
								}
							}

							if ( type === "first" ) { 
								return true; 
							}

							node = elem;

						case "last":
							while ( (node = node.nextSibling) )	 {
								if ( node.nodeType === 1 ) { 
									return false; 
								}
							}

							return true;

						case "nth":
							first = match[2];
							last = match[3];

							if ( first === 1 && last === 0 ) {
								return true;
							}
							
							doneName = match[0];
							parent = elem.parentNode;
			
							if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) {
								count = 0;
								
								for ( node = parent.firstChild; node; node = node.nextSibling ) {
									if ( node.nodeType === 1 ) {
										node.nodeIndex = ++count;
									}
								} 

								parent[ expando ] = doneName;
							}
							
							diff = elem.nodeIndex - last;

							if ( first === 0 ) {
								return diff === 0;

							} else {
								return ( diff % first === 0 && diff / first >= 0 );
							}
					}
				},

				ID: function( elem, match ) {
					return elem.nodeType === 1 && elem.getAttribute("id") === match;
				},

				TAG: function( elem, match ) {
					return (match === "*" && elem.nodeType === 1) || !!elem.nodeName && elem.nodeName.toLowerCase() === match;
				},
				
				CLASS: function( elem, match ) {
					return (" " + (elem.className || elem.getAttribute("class")) + " ")
						.indexOf( match ) > -1;
				},

				ATTR: function( elem, match ) {
					var name = match[1],
						result = Sizzle.attr ?
							Sizzle.attr( elem, name ) :
							Expr.attrHandle[ name ] ?
							Expr.attrHandle[ name ]( elem ) :
							elem[ name ] != null ?
								elem[ name ] :
								elem.getAttribute( name ),
						value = result + "",
						type = match[2],
						check = match[4];

					return result == null ?
						type === "!=" :
						!type && Sizzle.attr ?
						result != null :
						type === "=" ?
						value === check :
						type === "*=" ?
						value.indexOf(check) >= 0 :
						type === "~=" ?
						(" " + value + " ").indexOf(check) >= 0 :
						!check ?
						value && result !== false :
						type === "!=" ?
						value !== check :
						type === "^=" ?
						value.indexOf(check) === 0 :
						type === "$=" ?
						value.substr(value.length - check.length) === check :
						type === "|=" ?
						value === check || value.substr(0, check.length + 1) === check + "-" :
						false;
				},

				POS: function( elem, match, i, array ) {
					var name = match[2],
						filter = Expr.setFilters[ name ];

					if ( filter ) {
						return filter( elem, i, match, array );
					}
				}
			}
		};

		var origPOS = Expr.match.POS,
			fescape = function(all, num){
				return "\\" + (num - 0 + 1);
			};

		for ( var type in Expr.match ) {
			Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );
			Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );
		}

		var makeArray = function( array, results ) {
			array = Array.prototype.slice.call( array, 0 );

			if ( results ) {
				results.push.apply( results, array );
				return results;
			}
			
			return array;
		};

		// Perform a simple check to determine if the browser is capable of
		// converting a NodeList to an array using builtin methods.
		// Also verifies that the returned array holds DOM nodes
		// (which is not the case in the Blackberry browser)
		try {
			Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;

		// Provide a fallback method if it does not work
		} catch( e ) {
			makeArray = function( array, results ) {
				var i = 0,
					ret = results || [];

				if ( toString.call(array) === "[object Array]" ) {
					Array.prototype.push.apply( ret, array );

				} else {
					if ( typeof array.length === "number" ) {
						for ( var l = array.length; i < l; i++ ) {
							ret.push( array[i] );
						}

					} else {
						for ( ; array[i]; i++ ) {
							ret.push( array[i] );
						}
					}
				}

				return ret;
			};
		}

		var sortOrder, siblingCheck;

		if ( document.documentElement.compareDocumentPosition ) {
			sortOrder = function( a, b ) {
				if ( a === b ) {
					hasDuplicate = true;
					return 0;
				}

				if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
					return a.compareDocumentPosition ? -1 : 1;
				}

				return a.compareDocumentPosition(b) & 4 ? -1 : 1;
			};

		} else {
			sortOrder = function( a, b ) {
				// The nodes are identical, we can exit early
				if ( a === b ) {
					hasDuplicate = true;
					return 0;

				// Fallback to using sourceIndex (in IE) if it's available on both nodes
				} else if ( a.sourceIndex && b.sourceIndex ) {
					return a.sourceIndex - b.sourceIndex;
				}

				var al, bl,
					ap = [],
					bp = [],
					aup = a.parentNode,
					bup = b.parentNode,
					cur = aup;

				// If the nodes are siblings (or identical) we can do a quick check
				if ( aup === bup ) {
					return siblingCheck( a, b );

				// If no parents were found then the nodes are disconnected
				} else if ( !aup ) {
					return -1;

				} else if ( !bup ) {
					return 1;
				}

				// Otherwise they're somewhere else in the tree so we need
				// to build up a full list of the parentNodes for comparison
				while ( cur ) {
					ap.unshift( cur );
					cur = cur.parentNode;
				}

				cur = bup;

				while ( cur ) {
					bp.unshift( cur );
					cur = cur.parentNode;
				}

				al = ap.length;
				bl = bp.length;

				// Start walking down the tree looking for a discrepancy
				for ( var i = 0; i < al && i < bl; i++ ) {
					if ( ap[i] !== bp[i] ) {
						return siblingCheck( ap[i], bp[i] );
					}
				}

				// We ended someplace up the tree so do a sibling check
				return i === al ?
					siblingCheck( a, bp[i], -1 ) :
					siblingCheck( ap[i], b, 1 );
			};

			siblingCheck = function( a, b, ret ) {
				if ( a === b ) {
					return ret;
				}

				var cur = a.nextSibling;

				while ( cur ) {
					if ( cur === b ) {
						return -1;
					}

					cur = cur.nextSibling;
				}

				return 1;
			};
		}

		// Check to see if the browser returns elements by name when
		// querying by getElementById (and provide a workaround)
		(function(){
			// We're going to inject a fake input element with a specified name
			var form = document.createElement("div"),
				id = "script" + (new Date()).getTime(),
				root = document.documentElement;

			form.innerHTML = "<a name='" + id + "'/>";

			// Inject it into the root element, check its status, and remove it quickly
			root.insertBefore( form, root.firstChild );

			// The workaround has to do additional checks after a getElementById
			// Which slows things down for other browsers (hence the branching)
			if ( document.getElementById( id ) ) {
				Expr.find.ID = function( match, context, isXML ) {
					if ( typeof context.getElementById !== "undefined" && !isXML ) {
						var m = context.getElementById(match[1]);

						return m ?
							m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ?
								[m] :
								undefined :
							[];
					}
				};

				Expr.filter.ID = function( elem, match ) {
					var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");

					return elem.nodeType === 1 && node && node.nodeValue === match;
				};
			}

			root.removeChild( form );

			// release memory in IE
			root = form = null;
		})();

		(function(){
			// Check to see if the browser returns only elements
			// when doing getElementsByTagName("*")

			// Create a fake element
			var div = document.createElement("div");
			div.appendChild( document.createComment("") );

			// Make sure no comments are found
			if ( div.getElementsByTagName("*").length > 0 ) {
				Expr.find.TAG = function( match, context ) {
					var results = context.getElementsByTagName( match[1] );

					// Filter out possible comments
					if ( match[1] === "*" ) {
						var tmp = [];

						for ( var i = 0; results[i]; i++ ) {
							if ( results[i].nodeType === 1 ) {
								tmp.push( results[i] );
							}
						}

						results = tmp;
					}

					return results;
				};
			}

			// Check to see if an attribute returns normalized href attributes
			div.innerHTML = "<a href='#'></a>";

			if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
					div.firstChild.getAttribute("href") !== "#" ) {

				Expr.attrHandle.href = function( elem ) {
					return elem.getAttribute( "href", 2 );
				};
			}

			// release memory in IE
			div = null;
		})();

		if ( document.querySelectorAll ) {
			(function(){
				var oldSizzle = Sizzle,
					div = document.createElement("div"),
					id = "__sizzle__";

				div.innerHTML = "<p class='TEST'></p>";

				// Safari can't handle uppercase or unicode characters when
				// in quirks mode.
				if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
					return;
				}
			
				Sizzle = function( query, context, extra, seed ) {
					context = context || document;

					// Only use querySelectorAll on non-XML documents
					// (ID selectors don't work in non-HTML documents)
					if ( !seed && !Sizzle.isXML(context) ) {
						// See if we find a selector to speed up
						var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query );
						
						if ( match && (context.nodeType === 1 || context.nodeType === 9) ) {
							// Speed-up: Sizzle("TAG")
							if ( match[1] ) {
								return makeArray( context.getElementsByTagName( query ), extra );
							
							// Speed-up: Sizzle(".CLASS")
							} else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) {
								return makeArray( context.getElementsByClassName( match[2] ), extra );
							}
						}
						
						if ( context.nodeType === 9 ) {
							// Speed-up: Sizzle("body")
							// The body element only exists once, optimize finding it
							if ( query === "body" && context.body ) {
								return makeArray( [ context.body ], extra );
								
							// Speed-up: Sizzle("#ID")
							} else if ( match && match[3] ) {
								var elem = context.getElementById( match[3] );

								// Check parentNode to catch when Blackberry 4.6 returns
								// nodes that are no longer in the document #6963
								if ( elem && elem.parentNode ) {
									// Handle the case where IE and Opera return items
									// by name instead of ID
									if ( elem.id === match[3] ) {
										return makeArray( [ elem ], extra );
									}
									
								} else {
									return makeArray( [], extra );
								}
							}
							
							try {
								return makeArray( context.querySelectorAll(query), extra );
							} catch(qsaError) {}

						// qSA works strangely on Element-rooted queries
						// We can work around this by specifying an extra ID on the root
						// and working up from there (Thanks to Andrew Dupont for the technique)
						// IE 8 doesn't work on object elements
						} else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
							var oldContext = context,
								old = context.getAttribute( "id" ),
								nid = old || id,
								hasParent = context.parentNode,
								relativeHierarchySelector = /^\s*[+~]/.test( query );

							if ( !old ) {
								context.setAttribute( "id", nid );
							} else {
								nid = nid.replace( /'/g, "\\$&" );
							}
							if ( relativeHierarchySelector && hasParent ) {
								context = context.parentNode;
							}

							try {
								if ( !relativeHierarchySelector || hasParent ) {
									return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra );
								}

							} catch(pseudoError) {
							} finally {
								if ( !old ) {
									oldContext.removeAttribute( "id" );
								}
							}
						}
					}
				
					return oldSizzle(query, context, extra, seed);
				};

				for ( var prop in oldSizzle ) {
					Sizzle[ prop ] = oldSizzle[ prop ];
				}

				// release memory in IE
				div = null;
			})();
		}

		(function(){
			var html = document.documentElement,
				matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector;

			if ( matches ) {
				// Check to see if it's possible to do matchesSelector
				// on a disconnected node (IE 9 fails this)
				var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ),
					pseudoWorks = false;

				try {
					// This should fail with an exception
					// Gecko does not error, returns false instead
					matches.call( document.documentElement, "[test!='']:sizzle" );
			
				} catch( pseudoError ) {
					pseudoWorks = true;
				}

				Sizzle.matchesSelector = function( node, expr ) {
					// Make sure that attribute selectors are quoted
					expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']");

					if ( !Sizzle.isXML( node ) ) {
						try { 
							if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) {
								var ret = matches.call( node, expr );

								// IE 9's matchesSelector returns false on disconnected nodes
								if ( ret || !disconnectedMatch ||
										// As well, disconnected nodes are said to be in a document
										// fragment in IE 9, so check for that
										node.document && node.document.nodeType !== 11 ) {
									return ret;
								}
							}
						} catch(e) {}
					}

					return Sizzle(expr, null, null, [node]).length > 0;
				};
			}
		})();

		(function(){
			var div = document.createElement("div");

			div.innerHTML = "<div class='test e'></div><div class='test'></div>";

			// Opera can't find a second classname (in 9.6)
			// Also, make sure that getElementsByClassName actually exists
			if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
				return;
			}

			// Safari caches class attributes, doesn't catch changes (in 3.2)
			div.lastChild.className = "e";

			if ( div.getElementsByClassName("e").length === 1 ) {
				return;
			}
			
			Expr.order.splice(1, 0, "CLASS");
			Expr.find.CLASS = function( match, context, isXML ) {
				if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
					return context.getElementsByClassName(match[1]);
				}
			};

			// release memory in IE
			div = null;
		})();

		function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
			for ( var i = 0, l = checkSet.length; i < l; i++ ) {
				var elem = checkSet[i];

				if ( elem ) {
					var match = false;

					elem = elem[dir];

					while ( elem ) {
						if ( elem[ expando ] === doneName ) {
							match = checkSet[elem.sizset];
							break;
						}

						if ( elem.nodeType === 1 && !isXML ){
							elem[ expando ] = doneName;
							elem.sizset = i;
						}

						if ( elem.nodeName.toLowerCase() === cur ) {
							match = elem;
							break;
						}

						elem = elem[dir];
					}

					checkSet[i] = match;
				}
			}
		}

		function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
			for ( var i = 0, l = checkSet.length; i < l; i++ ) {
				var elem = checkSet[i];

				if ( elem ) {
					var match = false;
					
					elem = elem[dir];

					while ( elem ) {
						if ( elem[ expando ] === doneName ) {
							match = checkSet[elem.sizset];
							break;
						}

						if ( elem.nodeType === 1 ) {
							if ( !isXML ) {
								elem[ expando ] = doneName;
								elem.sizset = i;
							}

							if ( typeof cur !== "string" ) {
								if ( elem === cur ) {
									match = true;
									break;
								}

							} else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
								match = elem;
								break;
							}
						}

						elem = elem[dir];
					}

					checkSet[i] = match;
				}
			}
		}

		if ( document.documentElement.contains ) {
			Sizzle.contains = function( a, b ) {
				return a !== b && (a.contains ? a.contains(b) : true);
			};

		} else if ( document.documentElement.compareDocumentPosition ) {
			Sizzle.contains = function( a, b ) {
				return !!(a.compareDocumentPosition(b) & 16);
			};

		} else {
			Sizzle.contains = function() {
				return false;
			};
		}

		Sizzle.isXML = function( elem ) {
			// documentElement is verified for cases where it doesn't yet exist
			// (such as loading iframes in IE - #4833) 
			var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;

			return documentElement ? documentElement.nodeName !== "HTML" : false;
		};

		var posProcess = function( selector, context, seed ) {
			var match,
				tmpSet = [],
				later = "",
				root = context.nodeType ? [context] : context;

			// Position selectors must be done after the filter
			// And so must :not(positional) so we move all PSEUDOs to the end
			while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
				later += match[0];
				selector = selector.replace( Expr.match.PSEUDO, "" );
			}

			selector = Expr.relative[selector] ? selector + "*" : selector;

			for ( var i = 0, l = root.length; i < l; i++ ) {
				Sizzle( selector, root[i], tmpSet, seed );
			}

			return Sizzle.filter( later, tmpSet );
		};

		// EXPOSE

		fzxa.dom.query = Sizzle;

		})();

	}

})(this, this.document);
// end fzxa.js